﻿namespace DCHealthCheck.DomainModel.Tests {

    #region Usings
    using DCHealthCheck.Common;
    using DCHealthCheck.DomainModel;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure;
    using System.Data.Entity.Migrations;
    using System.Data.Objects;
    using System.Data.Objects.DataClasses;
    using System.Diagnostics;
    using System.Linq;
    using System.Management;
    using System.Security.Principal;
    using System.Text;
    #endregion

    [TestClass]
    public class Tests {

        #region Private methods
        #region Entity Creation Methods
        private Certificate CreateTestCertificate(EntityManager entityManager) {
            Certificate certificate = null;
            if (entityManager != null) {
                certificate = entityManager.Set<Certificate>().Create();
            }
            else {
                certificate = new Certificate();
            }

            certificate.DetachedState = DetachedState.Added;
            certificate.EffectiveDate = DateTime.UtcNow;
            certificate.ExpirationDate = DateTime.UtcNow.AddYears(1);
            certificate.FriendlyName = "CN=DCName, DC=contoso, DC=com";
            certificate.IssuerName = "CN=ContosoCA, DC=contoso, DC=com";
            certificate.PublicKey = "XXXXXXXXXXXXXX";
            certificate.SerialNumber = "FakeSerialNumber";
            certificate.SubjectName = "DCName.contoso.com";
            certificate.Thumbprint = "FakeThumbprint";
            certificate.X509CertificateRawData = Encoding.Unicode.GetBytes("Fake certificate data");

            return certificate;
        }

        private DomainController CreateTestDC(EntityManager entityManager) {
            var id = new Guid("C449A577-505E-470E-A9ED-9CFF496AC661");
            DomainController domainController = null;

            if (entityManager != null) {
                domainController = entityManager.Set<DomainController>().Find(id);
                if (domainController != null) {
                    Assert.IsTrue(domainController is IEntityWithChangeTracker);
                    entityManager.Detach(domainController);
                }
            }
            else {
                using (var privateEntityManager = new EntityManager()) {
                    domainController = privateEntityManager.Set<DomainController>().Find(id);
                    if (domainController != null) {
                        Assert.IsTrue(domainController is IEntityWithChangeTracker);
                        privateEntityManager.Detach(domainController);
                    }
                }
            }

            if (domainController == null) {
                domainController = new DomainController();

                domainController.Id = id;
                domainController.ADSite = "TestSite";
                domainController.DN = "CN=TESTDC,OU=Domain Controllers,DC=contoso,DC=com";
                domainController.DNSDomain = "contoso.com";
                domainController.DNSName = "contoso.com";
                domainController.NetbiosDomain = "CONTOSO";
                domainController.OSName = "Windows Server 2012 Standard";
                domainController.OSVersion = "6.2.9200";
                domainController.SamAccountName = "TESTDC";
                domainController.WhenCreated = DateTime.UtcNow;
                domainController.DetachedState = DetachedState.Added;
            }

            var validationResults = GetValidationResults(domainController);
            Assert.IsTrue(validationResults.Count == 0);

            return domainController;
        }

        private GroupPolicySysvolInfo CreateTestGroupPolicySysvolInfo(EntityManager entityManager) {
            GroupPolicySysvolInfo groupPolicySysvolInfo = null;
            if (entityManager != null) {
                groupPolicySysvolInfo = entityManager.Set<GroupPolicySysvolInfo>().Create();
            }
            else {
                groupPolicySysvolInfo = new GroupPolicySysvolInfo();
            }

            groupPolicySysvolInfo.CreatedUTC = DateTime.UtcNow;
            groupPolicySysvolInfo.DirectoryPath = @"C:\Path";
            groupPolicySysvolInfo.DisplayName = "Name";
            groupPolicySysvolInfo.ModifiedUTC = DateTime.UtcNow;
            groupPolicySysvolInfo.Version = "Version";
            groupPolicySysvolInfo.DetachedState = DetachedState.Added;

            var validationResults = GetValidationResults(groupPolicySysvolInfo);
            Assert.IsTrue(validationResults.Where(x => x.MemberNames.Any(y => (y != "Id") && (y != "SystemInfo"))).Count() == 0);

            return groupPolicySysvolInfo;
        }

        private HealthCheckSummary CreateTestHealthCheckSummary() {
            var healthCheckSummary = new HealthCheckSummary();
            healthCheckSummary.CheckDateTimeUTC = DateTime.UtcNow;
            healthCheckSummary.DetachedState = DetachedState.Added;
            healthCheckSummary.Summary = "Test HealthCheckSummary";
            return healthCheckSummary;
        }

        private InstalledProduct CreateTestInstalledProduct(EntityManager entityManager) {
            InstalledProduct installedProduct = null;

            if (entityManager != null) {
                installedProduct = entityManager.Set<InstalledProduct>().Create();
            }
            else {
                installedProduct = new InstalledProduct();
            }

            installedProduct.Description = "Description";
            installedProduct.IdentifyingNumber = Guid.NewGuid();
            installedProduct.InstallDate = DateTime.UtcNow;
            installedProduct.Name = "Name";
            installedProduct.Vendor = "Vendor";
            installedProduct.Version = "Version";
            installedProduct.DetachedState = DetachedState.Added;


            return installedProduct;
        }

        private ProcessInfo CreateTestProcessInfo(EntityManager entityManager) {
            ProcessInfo processInfo = null;
            if (entityManager != null) {
                processInfo = entityManager.Set<ProcessInfo>().Create();
            }
            else {
                processInfo = new ProcessInfo();
            }

            processInfo.CommandLine = "TestCommandLine";
            processInfo.ElapsedTimeTicks = 2348299;
            processInfo.ExecutablePath = "TestExecutablePath";
            processInfo.HandleCount = 88;
            processInfo.KernelModeTicks = 923;
            processInfo.UserModeTicks = 888;
            processInfo.NonPagePoolBytes = 999;
            processInfo.OtherBytes = 999;
            processInfo.PageFaults = 999;
            processInfo.PagePoolBytes = 999;
            processInfo.ParentProcessId = 7;
            processInfo.Priority = 8;
            processInfo.PrivateBytes = 999;
            processInfo.ProcessId = 8;
            processInfo.ReadBytes = 999;
            processInfo.StartDateTime = DateTime.UtcNow.AddHours(4);
            processInfo.ThreadCount = 7;
            processInfo.VirtualMemoryBytes = 999;
            processInfo.WorkingSetBytes = 999;
            processInfo.WriteBytes = 999;
            processInfo.DetachedState = DetachedState.Added;

            return processInfo;
        }

        private NetstatInfo CreateTestNetstatInfo() {
            var netstatInfo = new NetstatInfo();
            netstatInfo.DetachedState = DetachedState.Added;
            netstatInfo.ForeignAddress = "172.0.0.1";
            netstatInfo.ForeignPort = 80;
            netstatInfo.LocalAddress = "127.0.0.1";
            netstatInfo.LocalPort = 5999;
            netstatInfo.ProcessId = 777;
            netstatInfo.Protocol = ConnectionProtocol.TCP;
            netstatInfo.State = NetworkConnectionState.ESTABLISHED;

            return netstatInfo;
        }

        private PendingReplication CreateTestPendingReplication(EntityManager entityManager) {
            PendingReplication pendingReplication = null;

            if (entityManager != null) {
                pendingReplication = entityManager.Set<PendingReplication>().Create();
            }
            else {
                pendingReplication = new PendingReplication();
            }

            pendingReplication.OperationNumber = 1;
            pendingReplication.OperationType = "Type";
            pendingReplication.PartitionName = "Partition";
            pendingReplication.Priority = 23;
            pendingReplication.SourceServer = "Source";
            pendingReplication.TimeEnqueued = DateTime.UtcNow;
            pendingReplication.DetachedState = DetachedState.Added;


            return pendingReplication;
        }

        private ReplicationNeighbor CreateTestReplicationNeighbor(EntityManager entityManager) {
            ReplicationNeighbor replicationNeighbor = null;

            if (entityManager != null) {
                replicationNeighbor = entityManager.Set<ReplicationNeighbor>().Create();
            }
            else {
                replicationNeighbor = new ReplicationNeighbor();
            }

            replicationNeighbor.ConsecutiveFailureCount = 0;
            replicationNeighbor.ErrorMessage = string.Empty;
            replicationNeighbor.LastAttemptedSyncUTC = DateTime.UtcNow;
            replicationNeighbor.LastSuccessfulSyncUTC = DateTime.UtcNow;
            replicationNeighbor.LastSyncMessage = "Message";
            replicationNeighbor.LastSyncResult = 1;
            replicationNeighbor.PartitionName = "Partition";
            replicationNeighbor.ReplicationNeighborOption = "Option";
            replicationNeighbor.ReplicationReason = "Reason";
            replicationNeighbor.SourceInvocationId = Guid.NewGuid();
            replicationNeighbor.SourceServer = "Source";
            replicationNeighbor.USNAttributeFilter = 999;
            replicationNeighbor.USNLastObjectChangeSynced = 999;
            replicationNeighbor.DetachedState = DetachedState.Added;

            return replicationNeighbor;
        }

        private ServiceInfo CreateTestServiceInfo(EntityManager entityManager) {
            ServiceInfo serviceInfo = null;

            if (entityManager != null) {
                serviceInfo = entityManager.Set<ServiceInfo>().Create();
            }
            else {
                serviceInfo = new ServiceInfo();
            }

            serviceInfo.DisplayName = "DisplayName";
            serviceInfo.ErrorControl = ServiceErrorControl.Normal;
            serviceInfo.ExitCode = WINERROR.NO_ERROR;
            serviceInfo.Name = "Name";
            serviceInfo.PathName = "Path";
            serviceInfo.ProcessId = 8;
            serviceInfo.ServiceType = ServiceType.OwnProcess;
            serviceInfo.Started = true;
            serviceInfo.StartMode = ServiceStartMode.Auto;
            serviceInfo.StartName = "StartName";
            serviceInfo.State = ServiceState.Running;
            serviceInfo.Status = ServiceStatus.OK;
            serviceInfo.DetachedState = DetachedState.Added;


            return serviceInfo;
        }

        private RoleFeature CreateTestRoleFeature(EntityManager entityManager) {

            RoleFeature roleFeature = null;
            if (entityManager != null) {
                roleFeature = entityManager.Set<RoleFeature>().Create();
            }
            else {
                roleFeature = new RoleFeature();
            }

            roleFeature.DetachedState = DetachedState.Added;
            roleFeature.FeatureId = 100;
            roleFeature.Name = "TestRoleFeature";
            roleFeature.ParentFeatureId = 0;

            return roleFeature;
        }

        private SystemInfo CreateTestSystemInfo() {
            //SystemInfo systemInfo = entityManager.Set<SystemInfo>().Create();
            SystemInfo systemInfo = new SystemInfo();

            systemInfo.Id = Guid.NewGuid();
            systemInfo.CheckDateTimeUTC = DateTime.UtcNow;
            //DCTime = DateTime.UtcNow,
            //DCTimeUTC = DateTime.UtcNow,
            systemInfo.PingTest = TestResult.Fail;
            //DNSTest = TestResult.Fail.ToString(),
            //IsGlobalCatalog = Boolean.TrueString,
            //IsInfrastructureManager = Boolean.FalseString,
            //IsISTG = Boolean.FalseString,
            //IsPDCEmulator = Boolean.FalseString,
            //IsRIDManager = Boolean.FalseString,
            //IPAddress = "127.0.0.1",
            //OSInstallDate = DateTime.UtcNow,
            //StartupDateTimeUTC = DateTime.UtcNow,
            //TimeDiff = TimeSpan.Zero.ToString(),
            systemInfo.DetachedState = DetachedState.Added;

            var healthCheckSummary = CreateTestHealthCheckSummary();
            healthCheckSummary.CheckDateTimeUTC = systemInfo.CheckDateTimeUTC;
            systemInfo.HealthCheckSummary = healthCheckSummary;

            return systemInfo;
        }

        private SystemInfo CreateTestSystemInfo(EntityManager entityManager) {
            SystemInfo systemInfo = null;
            systemInfo = entityManager.Set<SystemInfo>().Create();
            Assert.IsTrue(systemInfo is IEntityWithChangeTracker);
            systemInfo.DetachedState = DetachedState.Added;
            systemInfo.PingTest = TestResult.Success;
            systemInfo.CheckDateTimeUTC = DateTime.UtcNow;

            var healthCheckSummary = CreateTestHealthCheckSummary();
            healthCheckSummary.CheckDateTimeUTC = systemInfo.CheckDateTimeUTC;
            systemInfo.HealthCheckSummary = healthCheckSummary;

            var groupPolicyInfo = CreateTestGroupPolicySysvolInfo(entityManager);
            //groupPolicyInfo.Id = systemInfo.Id;
            //groupPolicyInfo.SystemInfo = systemInfo;
            systemInfo.GroupPolicySysvolInfo = groupPolicyInfo;

            var installedProduct = CreateTestInstalledProduct(entityManager);
            //installedProduct.SystemInfo = systemInfo;
            systemInfo.InstalledProducts.Add(installedProduct);

            var pendingReplication = CreateTestPendingReplication(entityManager);
            //pendingReplication.SystemInfo = systemInfo;
            systemInfo.PendingReplications.Add(pendingReplication);

            var certificate = CreateTestCertificate(entityManager);
            systemInfo.Certificates.Add(certificate);

            var processInfo = CreateTestProcessInfo(entityManager);
            //processInfo.SystemInfo = systemInfo;
            systemInfo.ProcessInfos.Add(processInfo);

            var replicationNeighbor = CreateTestReplicationNeighbor(entityManager);
            //replicationNeighbor.SystemInfo = systemInfo;
            systemInfo.ReplicationNeighbors.Add(replicationNeighbor);

            var serviceInfo = CreateTestServiceInfo(entityManager);
            //serviceInfo.SystemInfo = systemInfo;
            systemInfo.ServiceInfos.Add(serviceInfo);

            var timeSyncInfo = CreateTestTimeSyncInfo(entityManager);
            //timeSyncInfo.Id = systemInfo.Id;
            //timeSyncInfo.SystemInfo = systemInfo;
            systemInfo.TimeSyncInfo = timeSyncInfo;

            var tagObject = CreateTestTagObject(entityManager);

            var tagObjectValue = CreateTestTagObjectValue();
            tagObjectValue.TagObject = tagObject;
            //tagObject.TagObjectValues.Add(tagObjectValue);

            //tagObjectValue.SystemInfo = systemInfo;
            systemInfo.TagObjectValues.Add(tagObjectValue);

            return systemInfo;
        }

        private SystemInfo CreateTestSystemInfoDetached() {
            SystemInfo systemInfo = null;
            using (var entityManager = new EntityManager()) {
                systemInfo = entityManager.Set<SystemInfo>().Create();
                //Assert.IsTrue(systemInfo is IEntityWithChangeTracker);
                entityManager.Detach(systemInfo);
            }

            systemInfo.DetachedState = DetachedState.Added;
            systemInfo.PingTest = TestResult.Success;
            systemInfo.CheckDateTimeUTC = DateTime.UtcNow;

            var healthCheckSummary = CreateTestHealthCheckSummary();
            healthCheckSummary.CheckDateTimeUTC = systemInfo.CheckDateTimeUTC;
            systemInfo.HealthCheckSummary = healthCheckSummary;

            var groupPolicyInfo = CreateTestGroupPolicySysvolInfo(null);
            //groupPolicyInfo.Id = systemInfo.Id;
            //groupPolicyInfo.SystemInfo = systemInfo;
            systemInfo.GroupPolicySysvolInfo = groupPolicyInfo;

            var installedProduct = CreateTestInstalledProduct(null);
            systemInfo.InstalledProducts.Add(installedProduct);

            var pendingReplication = CreateTestPendingReplication(null);
            systemInfo.PendingReplications.Add(pendingReplication);

            var certificate = CreateTestCertificate(null);
            systemInfo.Certificates.Add(certificate);            

            var processInfo = CreateTestProcessInfo(null);
            systemInfo.ProcessInfos.Add(processInfo);

            var replicationNeighbor = CreateTestReplicationNeighbor(null);
            systemInfo.ReplicationNeighbors.Add(replicationNeighbor);

            var serviceInfo = CreateTestServiceInfo(null);
            systemInfo.ServiceInfos.Add(serviceInfo);

            var timeSyncInfo = CreateTestTimeSyncInfo(null);
            //timeSyncInfo.SystemInfo = systemInfo;
            systemInfo.TimeSyncInfo = timeSyncInfo;

            var tagObject = CreateTestTagObject(null);

            var tagObjectValue = CreateTestTagObjectValue();
            tagObjectValue.TagObject = tagObject;
            //tagObjectValue.SystemInfo = systemInfo;
            systemInfo.TagObjectValues.Add(tagObjectValue);

            var netStatInfo = CreateTestNetstatInfo();
            //netStatInfo.ProcessId = processInfo.ProcessId;
            netStatInfo.ProcessInfo = processInfo;
            systemInfo.NetstatInfos.Add(netStatInfo);

            var secondNetStatInfo = CreateTestNetstatInfo();
            //secondNetStatInfo.ProcessId = processInfo.ProcessId;
            secondNetStatInfo.ProcessInfo = processInfo;
            systemInfo.NetstatInfos.Add(secondNetStatInfo);

            var roleFeature = CreateTestRoleFeature(null);
            systemInfo.RoleFeatures.Add(roleFeature);

            return systemInfo;
        }

        private TagObject CreateTestTagObject(EntityManager entityManager) {
            TagObject tagObject = null;

            string dn = "CN=DCHealthCheckTag,CN=Users,DC=acme,DC=com";

            if (entityManager != null) {
                tagObject = entityManager.Set<TagObject>().Find(dn);
                if (tagObject != null) {
                    //Assert.IsTrue(tagObject is IEntityWithChangeTracker);
                }
            }
            else {
                using (var privateEntityManager = new EntityManager()) {
                    tagObject = privateEntityManager.Set<TagObject>().Find(dn);
                    if (tagObject != null) {
                        //Assert.IsTrue(tagObject is IEntityWithChangeTracker);
                        privateEntityManager.Detach(tagObject);
                    }
                }
            }

            if (tagObject == null) {
                //tagObject = entityManager.Set<TagObject>().Create();
                tagObject = new TagObject();

                tagObject.DN = dn;
                tagObject.DNSDomain = "acme.com";
                tagObject.DetachedState = DetachedState.Added;
            }

            return tagObject;
        }

        private TagObjectValue CreateTestTagObjectValue() {
            var tagObjectValue = new TagObjectValue();

            tagObjectValue.TagValue = DateTime.UtcNow;
            tagObjectValue.DetachedState = DetachedState.Added;

            return tagObjectValue;
        }

        private TimeSyncInfo CreateTestTimeSyncInfo(EntityManager entityManager) {
            TimeSyncInfo timeSyncInfo = null;
            if (entityManager != null) {
                timeSyncInfo = entityManager.Set<TimeSyncInfo>().Create();
            }
            else {
                timeSyncInfo = new TimeSyncInfo();
            }

            timeSyncInfo.ClockRate = "ClockRate";
            timeSyncInfo.LastSuccessfulSync = DateTime.UtcNow;
            timeSyncInfo.LastSyncError = string.Empty;
            timeSyncInfo.LeapIndicator = "LeapIndicator";
            timeSyncInfo.PhaseOffset = "Offset";
            timeSyncInfo.PollInterval = "Poll";
            timeSyncInfo.Precision = "Precision";
            timeSyncInfo.RootDelay = "Delay";
            timeSyncInfo.RootDispersion = "Dispersion";
            timeSyncInfo.ServerRole = "Role";
            timeSyncInfo.Source = "Source";
            timeSyncInfo.SourceIPAddress = "SourceIP";
            timeSyncInfo.StateMachine = "StateMachine";
            timeSyncInfo.Stratum = "Primary";
            timeSyncInfo.TimeSinceLastGoodSync = "00:00:10";
            timeSyncInfo.TimeSourceFlags = "Flag";
            timeSyncInfo.DetachedState = DetachedState.Added;

            var validationResults = GetValidationResults(timeSyncInfo);
            Assert.IsTrue(validationResults.Where(x => x.MemberNames.Any(y => (y != "Id") && (y != "SystemInfo"))).Count() == 0);

            return timeSyncInfo;
        }

        private void DeleteTestEntity<TEntity>(TEntity entity) where TEntity : class, IObjectWithState {
            using (var entityManager = new EntityManager()) {
                var foundEntity = entityManager.Set<TEntity>().Find(entity.EntityKey);
                if (foundEntity != null) {
                    foundEntity.DetachedState = DetachedState.Deleted;
                    entityManager.Set<TEntity>().Remove(foundEntity);
                    entityManager.SaveChanges();
                }
            }
        }
        #endregion

        [TestMethod]
        private List<ValidationResult> GetValidationResults<TEntity>(TEntity entity) where TEntity : class, IValidatableObject, IObjectWithState {
            var validationResults = entity.Validate(null).ToList();

            bool isValid = IsValid<TEntity>(entity, validationResults);

            if (!isValid) {
                Console.WriteLine("Validation errors:");
                foreach (var result in validationResults) {
                    Console.WriteLine("-Property(s): {0} Validation Error Message: {1}",
                        result.MemberNames.ToList().ToDelimitedString(), result.ErrorMessage);
                }
            }

            return validationResults;
        }

        [TestMethod]
        private bool IsValid<TEntity>(TEntity entity, List<ValidationResult> validationResults) where TEntity : class, IValidatableObject, IObjectWithState {
            bool isValid = true;
            if (entity.DetachedState == DetachedState.Deleted) {
                isValid = validationResults
                    .Where(x => x.MemberNames
                        .Any(y => (y != "SystemInfo") && (y != "SystemInfoId")))
                    .Count() == 0;
            }
            else if (entity.DetachedState == DetachedState.Added) {
                isValid = validationResults
                    .Where(x => x.MemberNames
                        .Any(y => (y != "Id") && (y != "SystemInfo") && (y != "SystemInfoId")))
                    .Count() == 0;
            }
            else {
                isValid = validationResults
                    .Count() == 0;
            }

            return isValid;
        }
        #endregion

        #region Primitive Behaviors

        [TestMethod]
        public void TestChangeTracker() {
            using (var entityManager = new EntityManager()) {
                var domainController = entityManager.Set<DomainController>().Create();
                domainController = entityManager.Set<DomainController>().Add(domainController);

                var changeTrackerEntry = entityManager.Entry<DomainController>(domainController);
                var changeTrackerEntries = entityManager.ChangeTracker.Entries().ToList();
                Assert.IsNotNull(changeTrackerEntry);
                Assert.IsTrue(changeTrackerEntries.Count > 0);
                domainController = null;
            }
        }

        [TestMethod]
        public void TestDbContextObjectMaterialized() {
            using (var entityManager = new EntityManager()) {
                var dc = CreateTestDC(entityManager);
                entityManager.Set<DomainController>().Add(dc);
                entityManager.SaveChanges();

                dc = entityManager.Set<DomainController>().Find(dc.Id);

                Assert.IsNotNull(dc);
                Assert.IsTrue(dc.StartingOriginalValues.Count > 0);

                dc.DetachedState = DetachedState.Deleted;
                entityManager.Set<DomainController>().Remove(dc);

                entityManager.SaveChanges();
            }
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void TestIDisposable() {
            using (var entityManager = new EntityManager()) {
                var tempEntityManager = entityManager as IEntityManager;
                var disposable = tempEntityManager as IDisposable;
                Assert.IsNotNull(disposable);
                disposable.Dispose();

                // "The operation cannot be completed because the DbContext has been disposed"
                var dcCount = entityManager.QuerySets.DomainControllers.Count();
            }
        }

        /// <summary>
        /// If there is something wrong with the entity class/relationship configuration, these will throw an exception.
        /// </summary>
        [TestMethod]
        public void TestSchema() {
            using (var entityManager = new EntityManager()) {
                var audits = entityManager.Set<Audit>().ToList();
                var domainControllers = entityManager.Set<DomainController>().ToList();
                var systemInfos = entityManager.Set<SystemInfo>().Take(100).ToList();
                var groupPolicySysvolInfos = entityManager.Set<GroupPolicySysvolInfo>().Take(100).ToList();
                var healthCheckSummaries = entityManager.Set<HealthCheckSummary>().ToList();
                var installedProducts = entityManager.Set<InstalledProduct>().Take(100).ToList();
                var pendingReplications = entityManager.Set<PendingReplication>().Take(100).ToList();
                var processInfos = entityManager.Set<ProcessInfo>().Take(100).ToList();
                var replicationNeighbors = entityManager.Set<ReplicationNeighbor>().Take(100).ToList();
                var serviceInfos = entityManager.Set<ServiceInfo>().Take(100).ToList();
                var tagObjects = entityManager.Set<TagObject>().ToList();
                var tagObjectValues = entityManager.Set<TagObjectValue>().Take(100).ToList();
                var timeSyncInfos = entityManager.Set<TimeSyncInfo>().Take(100).ToList();

                var systemInfoCount = entityManager.QuerySets.SystemInfosSlimNoTracking.Count();
            }
        }

        #endregion

        [TestCleanup]
        public void TestCleanup() {
            using (var entityManager = new EntityManager()) {
                var healthCheckSummaries = entityManager.Set<HealthCheckSummary>()
                    .Where(x => x.Summary == "Test HealthCheckSummary")
                    .ToList();
                if (healthCheckSummaries.Count > 0) {
                    for (int index = 0; index < healthCheckSummaries.Count; index++) {
                        healthCheckSummaries[index].DetachedState = DetachedState.Deleted;
                        entityManager.Set<HealthCheckSummary>().Remove(healthCheckSummaries[index]);
                    }
                    entityManager.SaveChanges();
                }

                var testDCs = entityManager.Set<DomainController>()
                    .Where(x => x.DN == "CN=TESTDC,OU=Domain Controllers,DC=contoso,DC=com")
                    .ToList();
                if (testDCs.Count > 0) {
                    for (int index = 0; index < testDCs.Count; index++) {
                        testDCs[index].DetachedState = DetachedState.Deleted;
                        entityManager.Set<DomainController>().Remove(testDCs[index]);
                    }
                    entityManager.SaveChanges();
                }
            }
        }

        [TestMethod]
        public void TestClientValidation() {
            using (var entityManager = new EntityManager()) {
                var systemInfo = CreateTestSystemInfo(entityManager);
                var timeSyncInfo = CreateTestTimeSyncInfo(entityManager);

                var validationResults = GetValidationResults(timeSyncInfo);
                Assert.IsTrue(validationResults.Count == 2);

                timeSyncInfo.SystemInfo = systemInfo;

                validationResults = GetValidationResults(timeSyncInfo);
                Assert.IsTrue(validationResults.Count == 0);
            }
        }

        /// <summary>
        /// Create two SystemInfo entities, with related entities.  Detach the second SystemInfo.  Ensure that 
        /// the detached second SystemInfo and it's related entities do not produce any errors.
        /// </summary>
        [TestMethod]
        public void TestCreatingDetachedSystemInfoDoesNotFailValidationOnSave() {
            SystemInfo systemInfo = null;
            SystemInfo secondSystemInfo = null;
            DomainController dc = null;

            systemInfo = CreateTestSystemInfoDetached();
            secondSystemInfo = CreateTestSystemInfoDetached();

            secondSystemInfo.PingTest = TestResult.NotPerformed;
            secondSystemInfo.TagObjectValues.First().TagObject.TagObjectValues.Clear();
            secondSystemInfo.TagObjectValues.First().TagObject = null;
            secondSystemInfo.TagObjectValues.First().TagObject = systemInfo.TagObjectValues.First().TagObject;

            dc = CreateTestDC(null);

            using (var entityManager = new EntityManager()) {
                systemInfo.DomainController = dc;
                entityManager.Set<SystemInfo>().Add(systemInfo);

                entityManager.Detach(secondSystemInfo);
                entityManager.SaveChanges();
            }
        }

        [TestMethod]
        public void TestCreateDomainController() {
            using (var entityManager = new EntityManager()) {
                var dc = CreateTestDC(entityManager);
                var systemInfo = CreateTestSystemInfo(entityManager);
                systemInfo.DomainController = dc;
                entityManager.Set<DomainController>().Add(dc);
                entityManager.SaveChanges();

                Assert.IsNotNull(entityManager.QuerySets.DomainControllers.Where(x => x.Id == dc.Id).FirstOrDefault());

                dc.DetachedState = DetachedState.Deleted;
                entityManager.Set<DomainController>().Remove(dc);

                entityManager.SaveChanges();
            }
        }

        [TestMethod]
        public void TestCreateHealthCheckSummary() {
            using (var entityManager = new EntityManager()) {
                var healcheckSummaries = entityManager.QuerySets.HealthCheckSummaries.ToList();

                var healthCheckSummary = entityManager.Set<HealthCheckSummary>().Create();
                healthCheckSummary.DetachedState = DetachedState.Added;

                var targetDate = entityManager.QuerySets.SystemInfosSlim.First().CheckDateTimeUTC;
                var systemInfos = entityManager.QuerySets.SystemInfosSlim
                    .Where(x => x.CheckDateTimeUTC == targetDate)
                    .ToList();

                healthCheckSummary.CheckDateTimeUTC = systemInfos.First().CheckDateTimeUTC;
                healthCheckSummary.DCsTested = systemInfos.Count;
                healthCheckSummary.DCsWithErrors = systemInfos.Where(x => !string.IsNullOrWhiteSpace(x.ErrorMessage)).Count();
                healthCheckSummary.Summary = "Test";

                for (int index = 0; index < systemInfos.Count; index++) {
                    var systemInfo = systemInfos[index];
                    healthCheckSummary.SystemInfos.Add(systemInfo);
                }

                entityManager.SaveChanges();
            }
        }

        [TestMethod]
        public void TestCreateSystemInfos() {
            SystemInfo systemInfo = null;
            SystemInfo secondSystemInfo = null;
            DomainController dc = null;

            systemInfo = CreateTestSystemInfoDetached();
            dc = CreateTestDC(null);

            // create first systemInfo
            using (var entityManager = new EntityManager()) {
                // use .Add instead due to .Attach will result in 
                // System.InvalidOperationException: An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
                // entityManager.Attach<SystemInfo>(systemInfo);
                entityManager.Set<SystemInfo>().Add(systemInfo);
                systemInfo.DomainController = dc;

                var validationResults = GetValidationResults(systemInfo);
                Assert.IsTrue(validationResults.Count == 0);

                entityManager.SaveChanges();
            }

            // create second systemInfo that was created as an object in another context, then detached
            using (var entityManager = new EntityManager()) {
                secondSystemInfo = CreateTestSystemInfo(entityManager);
                secondSystemInfo.DomainController = entityManager.Set<DomainController>().Find(dc.Id);

                secondSystemInfo.DetachedState = DetachedState.Added;
                systemInfo.CheckDateTimeUTC = DateTime.UtcNow;
                entityManager.Set<SystemInfo>().Add(secondSystemInfo);

                var validationResults = GetValidationResults(secondSystemInfo);
                Assert.IsTrue(IsValid<SystemInfo>(secondSystemInfo, validationResults));

                entityManager.SaveChanges();

                secondSystemInfo = entityManager.Set<SystemInfo>().Where(x => x.Id == secondSystemInfo.Id).FirstOrDefault();
                Assert.IsNotNull(secondSystemInfo);
                Assert.IsNotNull(secondSystemInfo.DomainController);

                if (entityManager.Set<DomainController>().Find(secondSystemInfo.DomainController.Id) != null) {
                    secondSystemInfo.DomainController.DetachedState = DetachedState.Deleted;
                    entityManager.Set<DomainController>().Remove(secondSystemInfo.DomainController);
                }

                secondSystemInfo.DetachedState = DetachedState.Deleted;
                entityManager.Set<SystemInfo>().Remove(secondSystemInfo);

                validationResults = GetValidationResults(secondSystemInfo);
                Assert.IsTrue(IsValid<SystemInfo>(secondSystemInfo, validationResults));

                entityManager.SaveChanges();
            }
        }

        [TestMethod]
        public void TestCreateSystemInfoAndRemoveInvalidTagObjectValues() {
            using (var entityManager = new EntityManager()) {
                var dc = CreateTestDC(entityManager);
                var systemInfo = CreateTestSystemInfoDetached();
                systemInfo.DomainController = dc;

                var bogusTagObjectValue = CreateTestTagObjectValue();
                bogusTagObjectValue.TagValue = DateTime.MinValue;

                bogusTagObjectValue.TagObject = systemInfo.TagObjectValues.First().TagObject;
                systemInfo.TagObjectValues.Add(bogusTagObjectValue);

                entityManager.Set<SystemInfo>().Add(systemInfo);

                var tagObjectValues = systemInfo.TagObjectValues.ToList();
                for (int tagObjectValueIndex = 0; tagObjectValueIndex < tagObjectValues.Count; tagObjectValueIndex++) {
                    var tagObjectValue = tagObjectValues[tagObjectValueIndex];
                    if (tagObjectValue.TagValue == DateTime.MinValue) {
                        // if the entity was created as a change-tracking proxy, and we want to ensure that it is not
                        // included in the object graph for SaveChanges, it needs to be detached from the context
                        // in this manner.
                        if (systemInfo.TagObjectValues.Contains(tagObjectValue)) {
                            systemInfo.TagObjectValues.Remove(tagObjectValue);
                        }
                        if (tagObjectValue.TagObject.TagObjectValues.Contains(tagObjectValue)) {
                            tagObjectValue.TagObject.TagObjectValues.Remove(tagObjectValue);
                        }

                        entityManager.Detach(tagObjectValue);
                    }
                }

                entityManager.SaveChanges();

                Assert.IsNotNull(entityManager.QuerySets.SystemInfos.Where(x => x.Id == systemInfo.Id).FirstOrDefault());
            }
        }

        [TestMethod]
        public void TestDeleteCascade() {
            using (var entityManager = new EntityManager()) {
                var dc = CreateTestDC(entityManager);

                var systemInfo = CreateTestSystemInfoDetached();

                dc.SystemInfos.Add(systemInfo);

                dc = entityManager.Set<DomainController>().Add(dc);
                entityManager.SaveChanges();

                Assert.IsNotNull(dc);

                dc.DetachedState = DetachedState.Deleted;
                entityManager.Set<DomainController>().Remove(dc);
                entityManager.SaveChanges();

                Assert.IsNull(entityManager.Set<DomainController>().Find(dc.Id));
            }
        }

        [TestMethod]
        public void TestDeleteStubEntity() {
            DomainController dc = null;
            using (var entityManager = new EntityManager()) {
                dc = CreateTestDC(entityManager);
                entityManager.Set<DomainController>().Add(dc);
                entityManager.SaveChanges();

                dc = entityManager.Set<DomainController>()
                    .Where(x => x.Id == dc.Id)
                    .FirstOrDefault();

                Assert.IsNotNull(dc);
                Assert.IsTrue(dc.StartingOriginalValues.Count > 0);

            }
            
            // stubs are typically useful when the original context is no longer available
            using (var entityManager = new EntityManager()) {
                // if entity has a rowversion column, include that in the stub
                // it it is omitted, will produce the following exception:
                // System.Data.OptimisticConcurrencyException: Store update, insert, or delete statement affected an unexpected number of rows (0). 
                // Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.
                var dcToDelete = new DomainController() { Id = dc.Id, RowVersion = dc.RowVersion };
                dcToDelete.DetachedState = DetachedState.Deleted;
                entityManager.Attach<DomainController>(dcToDelete);
                entityManager.SaveChanges();
            }
        }

        [TestMethod]
        public void TestProxyCreation() {
            using (var entityManager = new EntityManager()) {
                var domainController = entityManager.Set<DomainController>().Create();
                Assert.IsTrue(domainController is IEntityWithChangeTracker);
                domainController = null;

                var groupPolicySysvolInfo = entityManager.Set<GroupPolicySysvolInfo>().Create();
                Assert.IsTrue(groupPolicySysvolInfo is IEntityWithChangeTracker);
                groupPolicySysvolInfo = null;

                var installedProduct = entityManager.Set<InstalledProduct>().Create();
                Assert.IsTrue(installedProduct is IEntityWithChangeTracker);
                installedProduct = null;

                var pendingReplication = entityManager.Set<PendingReplication>().Create();
                Assert.IsTrue(pendingReplication is IEntityWithChangeTracker);
                pendingReplication = null;

                var processInfo = entityManager.Set<ProcessInfo>().Create();
                Assert.IsTrue(processInfo is IEntityWithChangeTracker);
                processInfo = null;

                var replicationNeighbor = entityManager.Set<ReplicationNeighbor>().Create();
                Assert.IsTrue(replicationNeighbor is IEntityWithChangeTracker);
                replicationNeighbor = null;

                var serviceInfo = entityManager.Set<ServiceInfo>().Create();
                Assert.IsTrue(serviceInfo is IEntityWithChangeTracker);
                serviceInfo = null;

                var systemInfo = entityManager.Set<SystemInfo>().Create();
                Assert.IsTrue(systemInfo is IEntityWithChangeTracker);
                systemInfo = null;

                var tagObject = entityManager.Set<TagObject>().Create();
                Assert.IsTrue(tagObject is IEntityWithChangeTracker);
                tagObject = null;

                var tagObjectValue = entityManager.Set<TagObjectValue>().Create();
                Assert.IsTrue(tagObjectValue is IEntityWithChangeTracker);
                tagObjectValue = null;
            }
        }

        [TestMethod]
        public void TestReadAuditEntries() {
            using (var entityManager = new EntityManager()) {
                var audits = entityManager.AuditSet.ToList();

                if (audits.Count > 0) {
                    Assert.IsTrue(audits.All(x => !string.IsNullOrWhiteSpace(x.AuditEntryText)));
                }
            }
        }

        [TestMethod]
        public void TestUpdateSystemInfoWithPopulatedAssociations() {
            using (var entityManager = new EntityManager()) {
                var systemInfo = CreateTestSystemInfo();
                var dc = CreateTestDC(entityManager);

                systemInfo.DomainController = dc;
                entityManager.Set<SystemInfo>().Add(systemInfo);
                entityManager.SaveChanges();

                systemInfo = entityManager.QuerySets.SystemInfos.Where(x => x.Id == systemInfo.Id).FirstOrDefault();

                Assert.IsNotNull(systemInfo.StartingOriginalValues);

                systemInfo.DCFlags |= DCFlags.CloseSite;
                entityManager.SaveChanges();

                var dcFlagsFromStore = entityManager.QuerySets.SystemInfos.Where(x => x.Id == systemInfo.Id).FirstOrDefault().DCFlags;
                Assert.IsTrue(dcFlagsFromStore.HasFlag(DCFlags.CloseSite));

                dc.DetachedState = DetachedState.Deleted;
                entityManager.Set<DomainController>().Remove(dc);
                entityManager.SaveChanges();
            }
        }

        [TestMethod]
        public void TestUpdateDomainControllerUsingRowVersion() {
            // Arrange
            var identity = WindowsIdentity.GetCurrent();
            using (var entityManager = new EntityManager(identity)) {
                var dc = CreateTestDC(entityManager);
                var systemInfo = CreateTestSystemInfo(entityManager);
                systemInfo.DomainController = dc;

                entityManager.Set<DomainController>().Add(dc);
                entityManager.SaveChanges();

                Assert.IsNotNull(entityManager.QuerySets.DomainControllers.Where(x => x.Id == dc.Id).FirstOrDefault());

                // Act
                dc.ADSite += " Test ";
                entityManager.Set<DomainController>().Add(dc);

                // should only update the ADSite property.  Need SQL Profiler to verify.
                entityManager.SaveChanges();

                dc.DetachedState = DetachedState.Deleted;
                entityManager.Set<DomainController>().Remove(dc);
                entityManager.SaveChanges();
            }
        }

        #region Tests creating child entitites of SystemInfo
        [TestMethod]
        public void TestCreateCertificate() {
            using (var entityManager = new EntityManager()) {
                var dc = CreateTestDC(entityManager);
                var systemInfo = CreateTestSystemInfo(entityManager);
                systemInfo.DomainController = dc;

                dc.SystemInfos.Add(systemInfo);

                var certificate = CreateTestCertificate(entityManager);
                //certificate.SystemInfo = systemInfo;
                systemInfo.Certificates.Add(certificate);

                entityManager.Set<Certificate>().Add(certificate);
                entityManager.SaveChanges();

                Assert.IsNotNull(entityManager.QuerySets.Certificates.Where(x => x.Id == certificate.Id).FirstOrDefault());
            }
        }

        [TestMethod]
        public void TestCreateGroupPolicySysvolInfo() {
            using (var entityManager = new EntityManager()) {
                var dc = CreateTestDC(entityManager);
                var systemInfo = CreateTestSystemInfo(entityManager);
                systemInfo.DomainController = dc;

                dc.SystemInfos.Add(systemInfo);

                var groupPolicyInfo = CreateTestGroupPolicySysvolInfo(entityManager);
                groupPolicyInfo.Id = systemInfo.Id;
                groupPolicyInfo.SystemInfo = systemInfo;

                groupPolicyInfo = entityManager.Set<GroupPolicySysvolInfo>().Add(groupPolicyInfo);
                entityManager.SaveChanges();

                Assert.IsNotNull(groupPolicyInfo);
            }
        }

        [TestMethod]
        public void TestCreateInstalledProduct() {
            using (var entityManager = new EntityManager()) {
                var dc = CreateTestDC(entityManager);
                var systemInfo = CreateTestSystemInfo(entityManager);
                systemInfo.DomainController = dc;

                dc.SystemInfos.Add(systemInfo);

                var installedProduct = CreateTestInstalledProduct(entityManager);
                installedProduct.SystemInfo = systemInfo;
                systemInfo.InstalledProducts.Add(installedProduct);

                entityManager.Set<InstalledProduct>().Add(installedProduct);
                entityManager.SaveChanges();

                Assert.IsNotNull(entityManager.QuerySets.InstalledProducts.Where(x => x.Id == installedProduct.Id).FirstOrDefault());
            }
        }

        [TestMethod]
        public void TestCreatePendingReplication() {
            using (var entityManager = new EntityManager()) {
                var dc = CreateTestDC(entityManager);
                var systemInfo = CreateTestSystemInfo(entityManager);
                systemInfo.DomainController = dc;

                dc.SystemInfos.Add(systemInfo);

                var pendingReplication = CreateTestPendingReplication(entityManager);
                pendingReplication.SystemInfo = systemInfo;
                systemInfo.PendingReplications.Add(pendingReplication);

                var createdPendingReplication =
                entityManager.Set<PendingReplication>().Add(pendingReplication);
                entityManager.SaveChanges();

                Assert.IsNotNull(createdPendingReplication);
            }
        }

        [TestMethod]
        public void TestCreateProcessInfo() {
            using (var entityManager = new EntityManager()) {
                var dc = CreateTestDC(entityManager);
                var systemInfo = CreateTestSystemInfo(entityManager);
                systemInfo.DomainController = dc;

                dc.SystemInfos.Add(systemInfo);

                var processInfo = CreateTestProcessInfo(entityManager);
                processInfo.SystemInfo = systemInfo;
                systemInfo.ProcessInfos.Add(processInfo);

                entityManager.Set<ProcessInfo>().Add(processInfo);
                entityManager.SaveChanges();

                Assert.IsNotNull(entityManager.QuerySets.ProcessInfos.Where(x => x.Id == processInfo.Id).FirstOrDefault());
            }
        }

        [TestMethod]
        public void TestCreateReplicationNeighbor() {
            using (var entityManager = new EntityManager()) {
                var dc = CreateTestDC(entityManager);
                var systemInfo = CreateTestSystemInfo(entityManager);
                systemInfo.DomainController = dc;

                dc.SystemInfos.Add(systemInfo);

                var replicationNeighbor = CreateTestReplicationNeighbor(entityManager);
                replicationNeighbor.SystemInfo = systemInfo;
                systemInfo.ReplicationNeighbors.Add(replicationNeighbor);

                entityManager.Set<ReplicationNeighbor>().Add(replicationNeighbor);
                entityManager.SaveChanges();

                Assert.IsNotNull(entityManager.QuerySets.ReplicationNeighbors.Where(x => x.Id == replicationNeighbor.Id).FirstOrDefault());
            }
        }

        [TestMethod]
        public void TestCreateServiceInfo() {
            using (var entityManager = new EntityManager()) {
                var dc = CreateTestDC(entityManager);
                var systemInfo = CreateTestSystemInfo(entityManager);
                systemInfo.DomainController = dc;

                dc.SystemInfos.Add(systemInfo);

                var serviceInfo = CreateTestServiceInfo(entityManager);
                serviceInfo.SystemInfo = systemInfo;
                systemInfo.ServiceInfos.Add(serviceInfo);

                entityManager.Set<ServiceInfo>().Add(serviceInfo);
                entityManager.SaveChanges();


                Assert.IsNotNull(entityManager.QuerySets.ServiceInfos.Where(x => x.Id == serviceInfo.Id).FirstOrDefault());
            }
        }

        [TestMethod]
        public void TestCreateTimeSyncInfo() {
            using (var entityManager = new EntityManager()) {
                var dc = CreateTestDC(entityManager);
                var systemInfo = CreateTestSystemInfo(entityManager);
                systemInfo.DomainController = dc;

                dc.SystemInfos.Add(systemInfo);

                var timeSyncInfo = CreateTestTimeSyncInfo(entityManager);
                timeSyncInfo.Id = systemInfo.Id;
                timeSyncInfo.SystemInfo = systemInfo;
                systemInfo.TimeSyncInfo = timeSyncInfo;

                entityManager.Set<TimeSyncInfo>().Add(timeSyncInfo);
                entityManager.SaveChanges();

                Assert.IsNotNull(entityManager.QuerySets.TimeSyncInfos.Where(x => x.Id == timeSyncInfo.Id).FirstOrDefault());
            }
        }
        #endregion

        #region IRepository tests

        [TestMethod]
        public void TestSystemInfoRepositoryIncludes() {
            SystemInfo systemInfo = null;
            DomainController dc = null;

            systemInfo = CreateTestSystemInfoDetached();
            dc = CreateTestDC(null);

            //using (var entityManager = new EntityManager()) {
            using (var systemInfoRepository = new Repository<SystemInfo, EntityManager>(new EntityManager())) {
                systemInfo.DomainController = dc;
                var validationResults = GetValidationResults(systemInfo);
                Assert.IsTrue(validationResults.Count == 0);
                systemInfoRepository.Create(systemInfo);
                systemInfoRepository.Save();

                Assert.IsNotNull(systemInfoRepository.Get().Where(x => x.Id == systemInfo.Id).FirstOrDefault());
            }

            using (var systemInfoRepository = new Repository<SystemInfo, EntityManager>(new EntityManager())) {
                // specify includes: DomainController and HealthCheckSummary
                var systemInfos = systemInfoRepository
                    .Get(x => x.Id == systemInfo.Id, x => x.DomainController, x => x.HealthCheckSummary)
                    .ToList();

                Assert.IsTrue(systemInfos.Count > 0);
                Assert.IsNotNull(systemInfo.DomainController);
                Assert.IsNotNull(systemInfo.HealthCheckSummary);
            }

            // cleanup - remove TESTDC and all SystemInfo Objects
            using (var dcRepository = new Repository<DomainController, EntityManager>(new EntityManager())) {
                var dcToDelete = dcRepository.Find((x) => x.Id == dc.Id).FirstOrDefault();
                if (dcToDelete != null) {
                    dcRepository.Delete(dcToDelete);
                    dcRepository.Save();
                }
            }
        }
        #endregion
    }
}
